package main

import "fmt"

// defer

// 参考链接: https://books.studygolang.com/GoExpertProgramming/chapter02/2.1-defer.html

//  规则一：延迟函数的参数在defer语句出现时就已经确定下来(值拷贝), 无论后面如何修改aInt变量都不会影响延迟函数
func deferBeginFuncParameter() {
    var aInt = 1

    defer fmt.Println(aInt) // 1

    aInt = 2
    return
}

// 规则二：延迟函数执行按后进先出顺序执行，即先出现的defer最后执行
// 延迟函数printArray()的参数在defer语句出现时就已经确定了，即数组的地址;
// 由于延迟函数执行时机是在return语句之前，所以对数组的最终修改值会被打印出来
func printArray(array *[3]int) {
    for i := range array {
        fmt.Println(array[i])
    }
}

func deferStackFuncParameter() {
    var aArray = [3]int{1, 2, 3}
	var bArray = [3]int{4, 5, 6}
	var cArray = [3]int{7, 8, 9}

    defer printArray(&aArray) // 10 2 3      -> 根据栈原则，最后 被执行
	defer printArray(&bArray) // 4 5 6       -> 根据栈原则，其次 被执行
	defer printArray(&cArray) // 7 8 9       -> 根据栈原则，最先 被执行

    aArray[0] = 10
    return
}

// 规则三：延迟函数可能操作主函数的具名返回值
// 函数的return语句并不是原子的，实际执行分为设置返回值-->ret，defer语句实际执行在返回前;
// 即拥有defer的函数返回过程是：设置返回值-->执行defer-->ret。所以return语句先把result设置为i的值，即1，defer语句中又把result递增1，所以最终返回2
func deferFuncReturn() (result int) {
    i := 1

    defer func() {
       result++
	   fmt.Println(result) // 2
    }()

    return i
}

func main() {
	deferBeginFuncParameter()
	deferStackFuncParameter()
	fmt.Println(deferFuncReturn())
}
